-
Notifications
You must be signed in to change notification settings - Fork 73
Conversation
Codecov Report
@@ Coverage Diff @@
## master #327 +/- ##
=========================================
- Coverage 74.8% 74.71% -0.1%
=========================================
Files 14 15 +1
Lines 1691 1760 +69
Branches 317 331 +14
=========================================
+ Hits 1265 1315 +50
- Misses 280 294 +14
- Partials 146 151 +5
Continue to review full report at Codecov.
|
Could you add doc comments to all the symbols you added? Makes it easier to review |
src/project-manager.ts
Outdated
// TODO: add peer node_modules to source path. | ||
|
||
// TODO: determine how to expose this setting. | ||
// VS Code starts tsserver with --allowLocalPluginLoads by default: https://github.com/Microsoft/TypeScript/pull/15924 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would be good to have a setting for through didChangeWorkspaceConfiguration
, but it must be off by default or it would allow remote code execution.
b2dff0a
to
cc4c951
Compare
cc4c951
to
a0229ea
Compare
Plugin support: Plugin configuration: Plugin loading: Security: |
c94f134
to
21ac43e
Compare
@felixfbecker the plugins code was extracted and tests have been added, care to have a look? |
src/project-manager.ts
Outdated
* @param pluginModuleFactory function to create the module | ||
* @param configEntry extra settings from tsconfig to pass to the plugin module | ||
*/ | ||
private enableProxy(pluginModuleFactory: PluginModuleFactory, configEntry: ts.PluginImport) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this be called multiple times to wrap with multiple plugins?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, every time enableProxy is called, the current this.service
is wrapped, so the desired result is plugin2Wrapper(plugin1Wrapper(tsService))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A name like wrapService
would make it more clearer to me then, enable
always sounds like an idempotent function (like setting a boolean to true
)
src/project-manager.ts
Outdated
@@ -852,10 +864,39 @@ export class ProjectConfiguration { | |||
this.logger | |||
); | |||
this.service = ts.createLanguageService(this.host, this.documentRegistry); | |||
const pluginLoader = new PluginLoader(this.rootFilePath, this.fs, this.initializationOptions, this.logger); | |||
pluginLoader.loadPlugins(options, this.enableProxy.bind(this)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please use an arrow function, bind is not type safe
src/project-manager.ts
Outdated
const pluginModule = pluginModuleFactory({ typescript: ts }); | ||
this.service = pluginModule.create(info); | ||
} catch (e) { | ||
this.logger.info(`Plugin activation failed: ${e}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be an error
src/plugins.ts
Outdated
import { combinePaths } from './match-files'; | ||
import { InitializationOptions } from './request-type'; | ||
|
||
// definitions from from TypeScript server/project.ts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you add a full link to GitHub (with revision)
|
public loadPlugins(options: ts.CompilerOptions, applyProxy: EnableProxyFunc) { | ||
// Search our peer node_modules, then any globally-specified probe paths | ||
// ../../.. to walk from X/node_modules/javascript-typescript-langserver/lib/project-manager.js to X/node_modules/ | ||
const searchPaths = [combinePaths(__filename, '../../..'), ...this.pluginProbeLocations]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is the difference between combinePaths
and path.resolve()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code is copied from TS project, wanted to keep it as close to original as possible (and we had the function already)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, that wasn't clear to me. Maybe change the wording "based on" at the top of the file to "copied from"?
} | ||
|
||
// Provide global: true so plugins can detect why they can't find their config | ||
this.enablePlugin({ name: globalPluginName, global: true } as ts.PluginImport, searchPaths, applyProxy); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is global
not in the typings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code is copied from TS project - fix should be made there
*/ | ||
private enablePlugin(pluginConfigEntry: ts.PluginImport, searchPaths: string[], enableProxy: EnableProxyFunc) { | ||
for (const searchPath of searchPaths) { | ||
const resolvedModule = this.resolveModule(pluginConfigEntry.name, searchPath) as PluginModuleFactory; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this cast needed / why is resolveModule
not typed with that return type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolveModule
was not specific to plugin loading, it also was copied and I tried to keep it close to the original.
If it is exposed through the TS api we could remove it from this plugin.ts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there an open issue for exposing this? If not, could you create one and link it at the top?
src/plugins.ts
Outdated
private resolveJavaScriptModule(moduleName: string, initialDir: string, host: ts.ModuleResolutionHost): string { | ||
// TODO: this should set jsOnly=true to the internal resolver, but this parameter is not exposed on a public api. | ||
const result = | ||
ts.nodeModuleNameResolver(moduleName, /* containingFile */ initialDir.replace('\\', '/') + '/package.json', { moduleResolution: ts.ModuleResolutionKind.NodeJs, allowJs: true }, this.resolutionHost, undefined); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please wrap long lines by making one argument per line:
const foo = bar(
baz,
qux,
qaz
)
src/project-manager.ts
Outdated
@@ -852,10 +864,39 @@ export class ProjectConfiguration { | |||
this.logger | |||
); | |||
this.service = ts.createLanguageService(this.host, this.documentRegistry); | |||
const pluginLoader = new PluginLoader(this.rootFilePath, this.fs, this.pluginSettings, this.logger); | |||
pluginLoader.loadPlugins(options, this.enableProxy.bind(this) /* (factory, config) => this.enableProxy(factory, config)) */); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't use bind()
, it's not type safe. wrap in an arrow function instead
src/typescript-service.ts
Outdated
} | ||
export type Settings = { | ||
format: ts.FormatCodeSettings | ||
} & PluginSettings; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prefer interface Settings extends PluginSettings { ... }
Also clean up unused code adding TODOs
Exposes initializationOptions containing globalPlugins, allowLocalPluginLoads, pluginProbeLocations Also removes default config and outdated comments
…angeConfiguration
Also add 'node_modules' to resolvedPath Don't give up when resolveJavaScriptModule fails
e517506
to
d807ec3
Compare
Good to merge once lint failure is fixed |
Thanks for the work you put into merging yesterday! I'll try to clear up any of the outstanding issues with Typescript and ping you when the branch has received the possible short-term fixes. |
Test is fixed, confirmed it still works on Windows too! |
As some plugins also receive configuration options themselves (configFile, ignoreDefinitionFiles, etc., for tslint-language-service), "plugins": [
{
"name": "tslint-language-service",
"alwaysShowRuleFailuresAsWarnings": false,
"ignoreDefinitionFiles": true,
"configFile": "../tslint.json",
"disableNoUnusedVariableRule": false,
"supressWhileTypeErrorsPresent": false,
"mockTypeScriptVersion": false
}
] I'd suggest a way to be able to pass those to the plugin maybe passing the whole object here, prior maybe adding
What do you think? |
An investigation for #326
There are two ways of specifying plugins to be loaded:
plugins
declaration in tsconfig.json--globalPlugins
argument to tsserverThere are a few different ways to load the plugin:
--pluginProbeLocations
This PR contains implementation for loading a locally installed plugin specified in tsconfig.json only, as documented on Erich Gamma's new vscode plugin at https://github.com/Microsoft/vscode-ts-tslint.
Missing: